package com.itheima.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

//@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * BlockException异常处理器
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /***
     * 创建Sentinel过滤器
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /****
     * 规则初始化
     */
    @PostConstruct
    public void initRules(){
        initCustomizedApis();
        initGatewayRules();
    }


    /****
     * 自定义API->自定义资源
     */
    private void initCustomizedApis() {
        //定义API集合
        Set<ApiDefinition> definitions = new HashSet<>();
        //定义了API（自定义资源）
        ApiDefinition api = new ApiDefinition("item_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    //完整路径匹配
                    add(new ApiPathPredicateItem().setPattern("/item/city"));
                    //表示以/item/one/开始的所有请求
                    add(new ApiPathPredicateItem().setPattern("/item/one/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        definitions.add(api);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    /***
     * 对Gateway中指定的路由配置限流规则
     */
    //@PostConstruct
    public void initGatewayRules(){
        //网关限流规则
        Set<GatewayFlowRule> rules = new HashSet<GatewayFlowRule>();
        //商品微服务规则配置
        //资源名称，可以是网关中的 route 名称或者用户自定义的 API 分组名称
        rules.add(new GatewayFlowRule("order_route")
                //限流阈值
                .setCount(2)
                //应对突发请求时额外允许的请求数目。
                //.setBurst(2)
                //统计时间窗口，单位是秒，默认是 1 秒。
                .setIntervalSec(15)
                //限流行为
                //CONTROL_BEHAVIOR_RATE_LIMITER 匀速排队
                //CONTROL_BEHAVIOR_DEFAULT 快速失败(默认)
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
                //匀速排队模式下的最长排队时间，单位是毫秒，仅在匀速排队模式下生效。
                //.setMaxQueueingTimeoutMs(1000)
        );
        rules.add(new GatewayFlowRule("item_api")
                        //限流阈值
                        .setCount(2)
                        .setIntervalSec(15)
                        .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
        );
        GatewayRuleManager.loadRules(rules);
    }
}